package jnpf.engine.custom.behavior.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jnpf.engine.custom.constants.BasicActivitiConstants;
import jnpf.engine.custom.constants.GlobalProcessVariableConstants;
import jnpf.engine.custom.exception.PointAssigneeException;
import jnpf.engine.entity.FlowTemplateJsonEntity;
import jnpf.engine.enums.FlowSkipStrategyEnums;
import jnpf.engine.model.flowengine.FlowModel;
import jnpf.engine.model.flowengine.shuntjson.childnode.FlowAssignModel;
import jnpf.engine.model.flowengine.shuntjson.childnode.Properties;
import jnpf.engine.model.flowengine.shuntjson.childnode.RuleListModel;
import jnpf.engine.service.FlowTemplateJsonService;
import jnpf.engine.util.BpmnModelUtils;
import jnpf.engine.util.FlowContextHolder;
import jnpf.engine.util.FlowTaskUtil;
import jnpf.engine.util.SpringContextHolder;
import jnpf.engine.custom.helper.UserTaskNodeHelper;
import jnpf.exception.WorkFlowException;
import jnpf.model.flow.FlowFormDataModel;
import jnpf.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;

import java.util.*;

import static jnpf.engine.custom.constants.BasicActivitiConstants.AUTO_TRANSFER_ASSIGNEE;
import static jnpf.engine.custom.constants.BasicActivitiConstants.HQ_COUNTERSIGN_COMPLETE_CONDITION;
import static jnpf.engine.custom.constants.GlobalProcessVariableConstants.*;
import static jnpf.engine.custom.constants.RootExecutionVariableConstants.MULTI_INSTANCE_NUM_AUDIT;
import static jnpf.engine.custom.constants.RootExecutionVariableConstants.MULTI_INSTANCE_START_TIME;
import static jnpf.engine.custom.constants.TaskVariableConstants.COUNTER_SIGN_COMPLETE;
import static jnpf.engine.util.BpmnModelUtils.*;
import static jnpf.engine.util.EvalUtils.formData;
import static jnpf.util.TableFeildsEnum.FLOWID;

@Slf4j
public class CustomParallelMultiInstanceBehavior extends
        ParallelMultiInstanceBehavior {

    public CustomParallelMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior originalActivityBehavior) {
        super(activity, originalActivityBehavior);
    }

    @Override
    protected int createInstances(DelegateExecution execution) {
        String activityId = execution.getCurrentActivityId();
        String processDefinitionId = execution.getProcessDefinitionId();
        Process process = BpmnModelUtils.getBpmnModelMainProcess(processDefinitionId);
        UserTaskNodeHelper userTaskNodeHelper = SpringContextHolder.getBean(UserTaskNodeHelper.class);
        HistoryService historyService = SpringContextHolder.getBean(HistoryService.class);
        FlowTaskUtil flowTaskUtil = SpringContextHolder.getBean(FlowTaskUtil.class);
        ServiceAllUtil serviceAllUtil = SpringContextHolder.getBean(ServiceAllUtil.class);
        FlowElement currentFlowElement = execution.getCurrentFlowElement();
        String templateJsonId = execution.getVariable(FLOW_TEMPLATE_JSON_ID, String.class);
        if(currentFlowElement instanceof UserTask){
            UserTask activity = (UserTask) process.getFlowElement(activityId);
            String property = getAttributeValueByTaskDefinitionKey(process, activityId, "userTask");
            Properties properties = JSONObject.parseObject(property, new TypeReference<Properties>() {
            });
            Map<String, Object> data = new HashMap<>();
            String formId="";
            String formDataId="";
            Boolean taskFormEnabled = properties.getTaskFormEnabled();
            if(StringUtils.isBlank(properties.getFormId())){
                Properties firstNodeProperties = getFirstNodeProperty(execution.getProcessDefinitionId());
                formId=firstNodeProperties.getFormId();
                formDataId=execution.getProcessInstanceBusinessKey();
                Map<String, Object> firstNodeFormData = flowTaskUtil.infoData(formId, formDataId);
                if(CollUtil.isNotEmpty(firstNodeFormData)){
                    data.putAll(firstNodeFormData);
                }
            }
            else{
                formId=properties.getFormId();
                formDataId=execution.getId();
                //节点表单 二级实例
                if(taskFormEnabled==null ||Boolean.FALSE.equals(taskFormEnabled)){
                    List<FlowAssignModel> assignList = properties.getAssignList();
                    if(CollUtil.isNotEmpty(assignList)){
                        Map<String,Object> assignMap = new HashMap<>();
                        formData(assignMap, assignList,execution.getProcessInstanceId());
                        data.putAll(assignMap);

                        Map<String,Map<String, Object>> dbMap = new HashMap<>();
                        dbMap.put(formDataId + "_jnpf_" +formId ,data);
                        for (String idAll : dbMap.keySet()) {
                            String[] idList = idAll.split("_jnpf_");
                            Map<String, Object> formData = dbMap.get(idAll);
                            //
                            formData.put(FlowFormConstant.FLOWID, templateJsonId);
                            List<Map<String, Object>> formOperates = properties.getFormOperates();
                            FlowFormDataModel model = FlowFormDataModel.builder().formId(idList[1]).id(idList[0]).map(formData).formOperates(formOperates).build();
                            FlowContextHolder.addData(idList[0],formData);
                            serviceAllUtil.createOrUpdate(model);
                        }
                    }
                    else{
                        List<Map<String, Object>> formOperates = properties.getFormOperates();
                        Map<String,Object> map = new HashMap<>();
                        map.put(FlowFormConstant.FLOWID, templateJsonId);
                        FlowFormDataModel model = FlowFormDataModel.builder().formId(formId).id(formDataId).map(map).formOperates(formOperates).build();
                        FlowContextHolder.addData(formDataId,map);
                        serviceAllUtil.createOrUpdate(model);
                    }
                }
                else{
                    //说明开启的是 节点实例表单, 这个时候 只需要拿到相关数据 进行 变量找人或者接口找人数据传递  在任务监听器中进行赋值
                    List<FlowAssignModel> assignList = properties.getAssignList();
                    if(CollUtil.isNotEmpty(assignList)) {
                        Map<String, Object> assignMap = new HashMap<>();
                        formData(assignMap, assignList, execution.getProcessInstanceId());
                        data.putAll(assignMap);
                    }
                }
            }



            String pointAssigneeListVariableName=activityId+"PointAssigneeList";
            String variableName = activityId + "assigneeList";
            List<String> variable = execution.getVariable(pointAssigneeListVariableName, List.class);
            Integer assigneeType = properties.getAssigneeType();

            //两种情况,一种是 算是  正常分配人, 一种是 直接去干预节点, 比如 (上一个节点给下一个节点指定人, 鬼扯的发起人自选了,管理员进行流程指派,,,等等)
            List<String> assignUserList=new ArrayList<>();
            if(variable!=null){
                //不是空 在当前就2种情况 一种是 流程监控, 一种是候选人, 前者是threadlocal 后者是 变量
                Map<String, String> nodeUser = FlowContextHolder.getNodeUser();
                if(CollUtil.isEmpty(nodeUser)){
                    assignUserList.addAll(variable);
                }
                else{
                    //这里可以set 多个人, 只是JNPF前端传递了单个人而已
                    String assignee = MapUtil.getStr(nodeUser, activity.getId());
                    assignUserList.add(assignee);
                }
            }
            else{
                //按理来说 change接口 应该 也可以变更节点审批人的, 但是现在JNPF没有
                Map<String, String> nodeUser = FlowContextHolder.getNodeUser();
                if(CollUtil.isEmpty(nodeUser)){
                    FlowTemplateJsonService flowTemplateJsonService = SpringContextHolder.getBean(FlowTemplateJsonService.class);
                    LambdaQueryWrapper<FlowTemplateJsonEntity> lambdaQueryWrapper= new LambdaQueryWrapper<>();
                    lambdaQueryWrapper.eq(FlowTemplateJsonEntity::getProcessDefinitionId,execution.getProcessDefinitionId());
                    lambdaQueryWrapper.select(FlowTemplateJsonEntity::getTemplateId);
                    FlowTemplateJsonEntity templateJsonEntity = flowTemplateJsonService.getOne(lambdaQueryWrapper);
                    HashMap<String, String> taskInfoMap = MapUtil.newHashMap();
                    taskInfoMap.put("processDefinitionId",execution.getProcessDefinitionId());
                    taskInfoMap.put("taskDefinitionKey",activity.getId());
                    taskInfoMap.put("processInstanceId",execution.getProcessInstanceId());
                    Map<String, Object> variables = execution.getVariables();
                    taskInfoMap.put(APPLY_USER_ID,MapUtil.getStr(variables,APPLY_USER_ID));
                    taskInfoMap.put(BUSINESS_KEY,MapUtil.getStr(variables,BUSINESS_KEY));
                    assignUserList = userTaskNodeHelper.getAssignUserList(properties, taskInfoMap,templateJsonEntity.getTemplateId(),data);
                }
                else{
                    String assignee = MapUtil.getStr(nodeUser, activity.getId());
                    String[] split = assignee.split(",");
                    List<String> list =new ArrayList<>(Arrays.asList(split));
                    assignUserList.addAll(list);
                }
            }

            if(assignUserList.isEmpty()){
                String noAssignee = properties.getNoAssignee();
                if(StringUtils.isNotBlank(noAssignee)){
                    List<String> autoTransferAssignee = properties.getAutoTransferAssignee();
                    if(FlowSkipStrategyEnums.AUTO_COMPLETE.getStrategyCode().equals(noAssignee)){
                        assignUserList.add(AUTO_TRANSFER_ASSIGNEE);
                    }
                    else if(FlowSkipStrategyEnums.AUTO_REJECT.getStrategyCode().equals(noAssignee)){
                        assignUserList.add(AUTO_TRANSFER_ASSIGNEE);
                    }
                    else if(FlowSkipStrategyEnums.TRANSFER_ADMIN.getStrategyCode().equals(noAssignee)){
                        assignUserList.add("admin");
                    }
                    else if(FlowSkipStrategyEnums.TRANSFER_USERS.getStrategyCode().equals(noAssignee)){
                        List<String> list= new ArrayList<>();
                        list.addAll(properties.getApprovers());
                        List<String> handleIdAll = serviceAllUtil.getUserListAll(list);
                        assignUserList.addAll(handleIdAll);
                    }
                    else if(FlowSkipStrategyEnums.ERROR.getStrategyCode().equals(noAssignee)){
                        throw new WorkFlowException("节点上没有找到审批人,请检查配置");
                    }
                }
            }
            setLoopVariable(execution,variableName,assignUserList);
        }
        else if(currentFlowElement instanceof CallActivity){
            CallActivity activity = (CallActivity) process.getFlowElement(activityId);
            String property = getAttributeValueByTaskDefinitionKey(process, activityId, "callActivity");
            Properties properties = JSONObject.parseObject(property, new TypeReference<Properties>() {
            });
            Map<String, Object> data = new HashMap<>();
            String formId="";
            String formDataId="";
            Boolean taskFormEnabled = properties.getTaskFormEnabled();
            if(StringUtils.isBlank(properties.getFormId())){
                Properties firstNodeProperties = getFirstNodeProperty(execution.getProcessDefinitionId());
                formId=firstNodeProperties.getFormId();
                formDataId=execution.getProcessInstanceBusinessKey();
                Map<String, Object> firstNodeFormData = flowTaskUtil.infoData(formId, formDataId);
                if(CollUtil.isNotEmpty(firstNodeFormData)){
                    data.putAll(firstNodeFormData);
                    List<FlowAssignModel> assignList = properties.getAssignList();
                    Map<String, Object> map = formData(data, assignList, execution.getProcessInstanceId());
                    data.putAll(map);
                }
            }
            else{
                formId=properties.getFormId();
                formDataId=execution.getId();
                //节点表单 二级实例
                if(taskFormEnabled==null ||Boolean.FALSE.equals(taskFormEnabled)){
                    List<FlowAssignModel> assignList = properties.getAssignList();
                    if(CollUtil.isNotEmpty(assignList)){
                        Map<String,Object> assignMap = new HashMap<>();
                        formData(assignMap, assignList,execution.getProcessInstanceId());
                        data.putAll(assignMap);

                        Map<String,Map<String, Object>> dbMap = new HashMap<>();
                        dbMap.put(formDataId + "_jnpf_" +formId ,data);
                        for (String idAll : dbMap.keySet()) {
                            String[] idList = idAll.split("_jnpf_");
                            Map<String, Object> formData = dbMap.get(idAll);
                            //
                            formData.put(FlowFormConstant.FLOWID,templateJsonId);
                            List<Map<String, Object>> formOperates = properties.getFormOperates();
                            FlowFormDataModel model = FlowFormDataModel.builder().formId(idList[1]).id(idList[0]).map(formData).formOperates(formOperates).build();

                            FlowContextHolder.addData(idList[0],formData);
                            serviceAllUtil.createOrUpdate(model);
                        }
                    }
                    else{
                        List<Map<String, Object>> formOperates = properties.getFormOperates();
                        Map<String,Object> map = new HashMap<>();
                        map.put(FlowFormConstant.FLOWID, templateJsonId);
                        FlowFormDataModel model = FlowFormDataModel.builder().formId(formId).id(formDataId).map(map).formOperates(formOperates).build();
                        FlowContextHolder.addData(formDataId,map);
                        serviceAllUtil.createOrUpdate(model);
                    }
                }
                else{
                    //说明开启的是 节点实例表单, 这个时候 只需要拿到相关数据 进行 变量找人或者接口找人数据传递  在任务监听器中进行赋值
                    List<FlowAssignModel> assignList = properties.getAssignList();
                    if(CollUtil.isNotEmpty(assignList)) {
                        Map<String, Object> assignMap = new HashMap<>();
                        formData(assignMap, assignList, execution.getProcessInstanceId());
                        data.putAll(assignMap);
                    }
                }
            }



            String pointAssigneeListVariableName=activityId+"PointAssigneeList";
            String variableName = activityId + "assigneeList";
            List<String> variable = execution.getVariable(pointAssigneeListVariableName, List.class);
            Integer assigneeType = properties.getAssigneeType();

            //两种情况,一种是 算是  正常分配人, 一种是 直接去干预节点, 比如 (上一个节点给下一个节点指定人, 鬼扯的发起人自选了,管理员进行流程指派,,,等等)
            List<String> assignUserList=new ArrayList<>();
            if(variable!=null){
                //不是空 在当前就2种情况 一种是 流程监控, 一种是候选人, 前者是threadlocal 后者是 变量
                Map<String, String> nodeUser = FlowContextHolder.getNodeUser();
                if(CollUtil.isEmpty(nodeUser)){
                    assignUserList.addAll(variable);
                }
                else{
                    //这里可以set 多个人, 只是JNPF前端传递了单个人而已
                    String assignee = MapUtil.getStr(nodeUser, activity.getId());
                    assignUserList.add(assignee);
                }
            }
            else{
                //按理来说 change接口 应该 也可以变更节点审批人的, 但是现在JNPF没有
                Map<String, String> nodeUser = FlowContextHolder.getNodeUser();
                if(CollUtil.isEmpty(nodeUser)){
                    FlowTemplateJsonService flowTemplateJsonService = SpringContextHolder.getBean(FlowTemplateJsonService.class);
                    LambdaQueryWrapper<FlowTemplateJsonEntity> lambdaQueryWrapper= new LambdaQueryWrapper<>();
                    lambdaQueryWrapper.eq(FlowTemplateJsonEntity::getProcessDefinitionId,execution.getProcessDefinitionId());
                    lambdaQueryWrapper.select(FlowTemplateJsonEntity::getTemplateId);
                    FlowTemplateJsonEntity templateJsonEntity = flowTemplateJsonService.getOne(lambdaQueryWrapper);
                    HashMap<String, String> taskInfoMap = MapUtil.newHashMap();
                    taskInfoMap.put("processDefinitionId",execution.getProcessDefinitionId());
                    taskInfoMap.put("taskDefinitionKey",activity.getId());
                    taskInfoMap.put("processInstanceId",execution.getProcessInstanceId());
                    Map<String, Object> variables = execution.getVariables();
                    taskInfoMap.put(APPLY_USER_ID,MapUtil.getStr(variables,APPLY_USER_ID));
                    taskInfoMap.put(BUSINESS_KEY,MapUtil.getStr(variables,BUSINESS_KEY));
                    assignUserList = userTaskNodeHelper.getAssignUserList(properties, taskInfoMap,templateJsonEntity.getTemplateId(),data);
                }
                else{
                    String assignee = MapUtil.getStr(nodeUser, activity.getId());
                    assignUserList.add(assignee);
                }
            }

            if(assignUserList.isEmpty()){
                throw new WorkFlowException("子流程找不到审批人,请联系管理员重试");
            }
            setLoopVariable(execution,variableName,assignUserList);
        }
        if(HQ_COUNTERSIGN_COMPLETE_CONDITION.equals(completionCondition)){
            setLoopVariable(execution, COUNTER_SIGN_COMPLETE,Boolean.FALSE);
        }
        else{
            setLoopVariable(execution,MULTI_INSTANCE_NUM_AUDIT,0);
        }
        ExecutionEntityImpl executionEntity =(ExecutionEntityImpl) execution;
        Date startTime = executionEntity.getStartTime();
        String format = DateFormatUtils.format(startTime, "yyyy-MM-dd HH:mm:ss:SSS");
        execution.setVariable(activityId+MULTI_INSTANCE_START_TIME,format);
        int instances = super.createInstances(execution);
        return instances;
}



}
